#include "wordDistortionGenerator.h"
#include "editErrorCostHolders.h"

#include <iterator>

void supportedAlphabetHolder::Alphabet( std::vector<char>& _alphabet )
{
	_alphabet.clear();
	for( unsigned alphSymb = 0; alphSymb < 'z' - 'a' + 1; ++alphSymb ) _alphabet.push_back( 'a' + alphSymb );
}

void wordDistortionGenerator::generateOneEditOperatonDistortion( const std::string& token,
	std::vector<std::string>& distortion, bool allowTransposition ) const
{
	std::set<std::string> result;
	addTokenDistortionToSet( token, result, allowTransposition );
	distortion.clear();
	std::copy( result.begin(), result.end(), std::back_inserter( distortion ) );
}

void wordDistortionGenerator::generateTwoEditOperatonLocalityDistortion(
	const std::string& token, std::vector<std::string>& distortion, bool allowTransposition ) const
{
	std::set<std::string> oneEODistortion;
	addTokenDistortionToSet( token, oneEODistortion, allowTransposition );
	std::set<std::string> result( oneEODistortion );
	std::set<std::string>::const_iterator it;
	for( it = oneEODistortion.begin(); it != oneEODistortion.end(); ++it ) {
		addTokenDistortionToSet( *it, result, allowTransposition );
	}
	distortion.clear();
	std::copy( result.begin(), result.end(), std::back_inserter( distortion ) );
}

void wordDistortionGenerator::generateTokenCuts( const std::string& token, std::vector<std::pair<std::string, std::string> >& cuts ) const
{
	cuts.clear();
	for( size_t symbIndex = 1; symbIndex < token.length(); ++symbIndex ) {
		cuts.push_back( std::pair<std::string, std::string>( token.substr( 0, symbIndex ), token.substr( symbIndex ) ) );
	}	
}

void wordDistortionGenerator::addTokenDistortionToSet( const std::string& token, std::set<std::string>& _set, bool allowTransposition ) const
{
	for( size_t symbIndex = 0; symbIndex < token.length(); ++symbIndex ) {
		std::string tokenAfterDeletion( token );
		tokenAfterDeletion.erase( tokenAfterDeletion.begin() + symbIndex );
		_set.insert( tokenAfterDeletion );
		for( size_t charIndex = 0; charIndex < alphabet.size(); ++charIndex ) {
			std::string tokenAfterInsertion( token );
			tokenAfterInsertion.insert( tokenAfterInsertion.begin() + symbIndex, alphabet[charIndex] );
			_set.insert( tokenAfterInsertion );
			std::string tokenAfterSubstitution( token );
			if( alphabet[charIndex] != token[symbIndex] ) {
				tokenAfterSubstitution[symbIndex] = alphabet[charIndex];
				_set.insert( tokenAfterSubstitution );
			}
		}
		for( size_t charIndex = 0; charIndex < alphabet.size(); ++charIndex ) {
			std::string tokenAfterInsertion( token );
			tokenAfterInsertion.insert( tokenAfterInsertion.begin() + token.length(), alphabet[charIndex] );
			_set.insert( tokenAfterInsertion );
		}
		if( allowTransposition && symbIndex + 1 != token.length() ) {
			std::string tokenAfterTransposition( token );
			tokenAfterTransposition[symbIndex] = token[symbIndex + 1];
			tokenAfterTransposition[symbIndex + 1] = token[symbIndex];
			_set.insert( tokenAfterTransposition );
		}
	}
}

void wordDistortionGenerator::generateWeightedOneEditOperatonDistortion(
	const std::string& token,
	std::vector<std::string>& distortion,
	std::vector<double>& weightes,
	bool allowTransposition ) const
{
	distortion.clear();
	weightes.clear();
	std::map<std::string, double> tokenToWeight;
	addWeightedTokenDistortionToMap( token, 0.0, tokenToWeight, allowTransposition );
	std::map<std::string, double>::iterator it;
	tokenToWeight[token] = 0.0; 
	for( it = tokenToWeight.begin(); it != tokenToWeight.end(); ++it ) {
		distortion.push_back( (*it).first );
		weightes.push_back( (*it).second );
	}
}

void wordDistortionGenerator::generateWeightedTwoEditOperatonLocalityDistortion(
	const std::string& token,
	std::vector<std::string>& distortion,
	std::vector<double>& weightes,
	bool allowTransposition ) const
{
	distortion.clear();
	weightes.clear();
	std::map<std::string, double> oneErrLocalityTokenToWeight;
	addWeightedTokenDistortionToMap( token, 0.0, oneErrLocalityTokenToWeight, allowTransposition );
	std::map<std::string, double> tokenToWeight( oneErrLocalityTokenToWeight );

	std::map<std::string, double>::iterator it;
	for( it = oneErrLocalityTokenToWeight.begin(); it != oneErrLocalityTokenToWeight.end(); ++it ) {
    if (it->second < 1.0) {
		  addWeightedTokenDistortionToMap( (*it).first, (*it).second, tokenToWeight, allowTransposition );
    }
	}
	tokenToWeight[token] = 0.0;
	for( it = tokenToWeight.begin(); it != tokenToWeight.end(); ++it ) {
		distortion.push_back( (*it).first );
		weightes.push_back( (*it).second );
	}
}

void wordDistortionGenerator::addWeightedTokenDistortionToMap(
	const std::string& token, double currErr, std::map<std::string, double>& _map, bool allowTransposition ) const
{
	//keyboardErrorsCostHolder errHolder;
  phoneticErrorsCostHolder errHolder;
	for( size_t symbIndex = 1; symbIndex < token.length(); ++symbIndex ) {
		std::string tokenAfterDeletion( token );
		tokenAfterDeletion.erase( tokenAfterDeletion.begin() + symbIndex );
		if( _map.count( tokenAfterDeletion ) == 0 ) {
			double delCost;
			if( symbIndex + 1 == token.length() ) {
				delCost = 1.0;
			} else {
				delCost = errHolder.deletionCost( token[symbIndex + 1] ,token[symbIndex] );
			}
			_map[tokenAfterDeletion] = currErr + delCost;
		}
		for( size_t charIndex = 0; charIndex < alphabet.size(); ++charIndex ) {
			std::string tokenAfterInsertion( token );
			tokenAfterInsertion.insert( tokenAfterInsertion.begin() + symbIndex, alphabet[charIndex] );
			if( _map.count( tokenAfterInsertion ) == 0 ) {
				_map[tokenAfterInsertion] = currErr + errHolder.insertionCost( token[symbIndex], alphabet[charIndex] );
			}
			std::string tokenAfterSubstitution( token );
			if( alphabet[charIndex] != token[symbIndex] ) {
				tokenAfterSubstitution[symbIndex] = alphabet[charIndex];
				if( _map.count( tokenAfterSubstitution ) == 0 ) {
					_map[tokenAfterSubstitution] = currErr + errHolder.substitutionCost( token[symbIndex], alphabet[charIndex] );
				}
			}
		}
		for( size_t charIndex = 0; charIndex < alphabet.size(); ++charIndex ) {
			std::string tokenAfterInsertion( token );
			tokenAfterInsertion.insert( tokenAfterInsertion.begin() + token.length(), alphabet[charIndex] );
			if( _map.count( tokenAfterInsertion ) == 0 ) {
				_map[tokenAfterInsertion] = currErr + errHolder.insertionCost( token[token.length() - 1], alphabet[charIndex] );
			}
		}
		if( allowTransposition && symbIndex + 1 != token.length() ) {
			std::string tokenAfterTransposition( token );
			tokenAfterTransposition[symbIndex] = token[symbIndex + 1];
			tokenAfterTransposition[symbIndex + 1] = token[symbIndex];
			if( _map.count( tokenAfterTransposition ) == 0 ) {
				_map[tokenAfterTransposition] = currErr + errHolder.substitutionCost( token[symbIndex], token[symbIndex + 1] );
			}
		}
	}
}

